gsk: Fix the gl texture cache
authorMatthias Clasen <mclasen@redhat.com>
Wed, 9 Sep 2020 17:55:09 +0000 (13:55 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Wed, 9 Sep 2020 17:55:09 +0000 (13:55 -0400)
We need to include both the scale and the filtering
in the key for the texture cache, since those affect
the texture.

This fixes misrendering in the recorder in the inspector
whenever transforms are involved. An example where this
was showing up is testrevealer's swing transition.

gsk/gl/gskgldriver.c
gsk/gl/gskgldriverprivate.h
gsk/gl/gskglrenderer.c

index 08e19a980f97b1d147d563d3b198f03f4dbc2520..8d101b93df504f6fc68163adbe07bdc107b90118 100644 (file)
@@ -541,16 +541,37 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
   return t->texture_id;
 }
 
+static guint
+texture_key_hash (gconstpointer v)
+{
+  const GskTextureKey *k = (GskTextureKey *)v;
+
+  return GPOINTER_TO_UINT (k->pointer)
+         + (guint)(k->scale*100)
+         + (guint)k->filter;
+}
+
+static gboolean
+texture_key_equal (gconstpointer v1, gconstpointer v2)
+{
+  const GskTextureKey *k1 = (GskTextureKey *)v1;
+  const GskTextureKey *k2 = (GskTextureKey *)v2;
+
+  return k1->pointer == k2->pointer &&
+         k1->scale == k2->scale &&
+         k1->filter == k2->filter;
+}
+
 int
-gsk_gl_driver_get_texture_for_pointer (GskGLDriver *self,
-                                       gpointer     pointer)
+gsk_gl_driver_get_texture_for_key (GskGLDriver   *self,
+                                   GskTextureKey *key)
 {
   int id = 0;
 
   if (G_UNLIKELY (self->pointer_textures == NULL))
-    self->pointer_textures = g_hash_table_new (NULL, NULL);
+    self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
 
-  id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, pointer));
+  id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, key));
 
   if (id != 0)
     {
@@ -566,14 +587,19 @@ gsk_gl_driver_get_texture_for_pointer (GskGLDriver *self,
 }
 
 void
-gsk_gl_driver_set_texture_for_pointer (GskGLDriver *self,
-                                       gpointer     pointer,
-                                       int          texture_id)
+gsk_gl_driver_set_texture_for_key (GskGLDriver   *self,
+                                   GskTextureKey *key,
+                                   int            texture_id)
 {
+  GskTextureKey *k;
+
   if (G_UNLIKELY (self->pointer_textures == NULL))
-    self->pointer_textures = g_hash_table_new (NULL, NULL);
+    self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
+
+  k = g_new (GskTextureKey, 1);
+  *k = *key;
 
-  g_hash_table_insert (self->pointer_textures, pointer, GINT_TO_POINTER (texture_id));
+  g_hash_table_insert (self->pointer_textures, k, GINT_TO_POINTER (texture_id));
 }
 
 int
index 22c5ef8cb334b5ca8c5bed2473edee145fe39bcd..406b95937283fda0d939a6ecba2b6d7fe483730e 100644 (file)
@@ -21,6 +21,11 @@ typedef struct {
   guint texture_id;
 } TextureSlice;
 
+typedef struct {
+  gpointer pointer;
+  float scale;
+  int filter;
+} GskTextureKey;
 
 GskGLDriver *   gsk_gl_driver_new                       (GdkGLContext    *context);
 GdkGLContext   *gsk_gl_driver_get_gl_context            (GskGLDriver     *driver);
@@ -34,10 +39,10 @@ int             gsk_gl_driver_get_texture_for_texture   (GskGLDriver     *driver
                                                          GdkTexture      *texture,
                                                          int              min_filter,
                                                          int              mag_filter);
-int             gsk_gl_driver_get_texture_for_pointer   (GskGLDriver     *driver,
-                                                         gpointer         pointer);
-void            gsk_gl_driver_set_texture_for_pointer   (GskGLDriver     *driver,
-                                                         gpointer         pointer,
+int             gsk_gl_driver_get_texture_for_key       (GskGLDriver     *driver,
+                                                         GskTextureKey   *key);
+void            gsk_gl_driver_set_texture_for_key       (GskGLDriver     *driver,
+                                                         GskTextureKey   *key,
                                                          int              texture_id);
 int             gsk_gl_driver_create_texture            (GskGLDriver     *driver,
                                                          float            width,
index 27d1c48cbdefbfa9e27551d0fef0c142f8dd6e93..edc2fd26188c0d27f4cf10142ba3e33307abb496 100644 (file)
@@ -572,12 +572,17 @@ render_fallback_node (GskGLRenderer   *self,
   cairo_t *cr;
   int cached_id;
   int texture_id;
+  GskTextureKey key;
 
   if (surface_width <= 0 ||
       surface_height <= 0)
     return;
 
-  cached_id = gsk_gl_driver_get_texture_for_pointer (self->gl_driver, node);
+  key.pointer = node;
+  key.scale = scale;
+  key.filter = GL_NEAREST;
+
+  cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
 
   if (cached_id != 0)
     {
@@ -659,7 +664,7 @@ render_fallback_node (GskGLRenderer   *self,
   cairo_surface_destroy (surface);
   cairo_surface_destroy (rendered_surface);
 
-  gsk_gl_driver_set_texture_for_pointer (self->gl_driver, node, texture_id);
+  gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id);
 
   ops_set_program (builder, &self->programs->blit_program);
   ops_set_texture (builder, texture_id);
@@ -1638,6 +1643,7 @@ render_blur_node (GskGLRenderer   *self,
   const float blur_radius = gsk_blur_node_get_radius (node);
   GskRenderNode *child = gsk_blur_node_get_child (node);
   TextureRegion blurred_region;
+  GskTextureKey key;
 
   if (node_is_invisible (child))
     return;
@@ -1648,7 +1654,10 @@ render_blur_node (GskGLRenderer   *self,
       return;
     }
 
-  blurred_region.texture_id = gsk_gl_driver_get_texture_for_pointer (self->gl_driver, node);
+  key.pointer = node;
+  key.scale = ops_get_scale (builder);
+  key.filter = GL_NEAREST;
+  blurred_region.texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
   if (blurred_region.texture_id == 0)
     blur_node (self, child, builder, blur_radius, 0, &blurred_region, NULL);
 
@@ -1660,7 +1669,7 @@ render_blur_node (GskGLRenderer   *self,
   load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); /* Render result to screen */
 
   /* Add to cache for the blur node */
-  gsk_gl_driver_set_texture_for_pointer (self->gl_driver, node, blurred_region.texture_id);
+  gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_region.texture_id);
 }
 
 static inline void
@@ -1698,13 +1707,17 @@ render_inset_shadow_node (GskGLRenderer   *self,
   float texture_width;
   float texture_height;
   int blurred_texture_id;
+  GskTextureKey key;
 
   g_assert (blur_radius > 0);
 
   texture_width = ceilf ((node_outline->bounds.size.width + blur_extra) * scale);
   texture_height = ceilf ((node_outline->bounds.size.height + blur_extra) * scale);
 
-  blurred_texture_id = gsk_gl_driver_get_texture_for_pointer (self->gl_driver, node);
+  key.pointer = node;
+  key.scale = scale;
+  key.filter = GL_NEAREST;
+  blurred_texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
   if (blurred_texture_id == 0)
     {
       const float spread = gsk_inset_shadow_node_get_spread (node) + (blur_extra / 2.0);
@@ -1800,7 +1813,7 @@ render_inset_shadow_node (GskGLRenderer   *self,
     const float ty1 = blur_extra / 2.0 * scale / texture_height;
     const float ty2 = 1.0 - ty1;
 
-    gsk_gl_driver_set_texture_for_pointer (self->gl_driver, node, blurred_texture_id);
+    gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, blurred_texture_id);
 
     if (needs_clip)
       {
@@ -3400,6 +3413,8 @@ add_offscreen_ops (GskGLRenderer         *self,
   int texture_id = 0;
   int max_texture_size;
   int filter;
+  GskTextureKey key;
+  int cached_id;
 
   if (node_is_invisible (child_node))
     {
@@ -3421,18 +3436,24 @@ add_offscreen_ops (GskGLRenderer         *self,
       return TRUE;
     }
 
+  if (flags & LINEAR_FILTER)
+    filter = GL_LINEAR;
+  else
+    filter = GL_NEAREST;
+
   /* Check if we've already cached the drawn texture. */
-  {
-    const int cached_id = gsk_gl_driver_get_texture_for_pointer (self->gl_driver, child_node);
+  key.pointer = child_node;
+  key.scale = ops_get_scale (builder);
+  key.filter = filter;
+  cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
 
-    if (cached_id != 0)
-      {
-        init_full_texture_region (texture_region_out, cached_id);
-        /* We didn't render it offscreen, but hand out an offscreen texture id */
-        *is_offscreen = TRUE;
-        return TRUE;
-      }
-  }
+  if (cached_id != 0)
+    {
+      init_full_texture_region (texture_region_out, cached_id);
+      /* We didn't render it offscreen, but hand out an offscreen texture id */
+      *is_offscreen = TRUE;
+      return TRUE;
+    }
 
   scale = ops_get_scale (builder);
   width = bounds->size.width;
@@ -3452,10 +3473,6 @@ add_offscreen_ops (GskGLRenderer         *self,
   width  = ceilf (width * scale);
   height = ceilf (height * scale);
 
-  if (flags & LINEAR_FILTER)
-    filter = GL_LINEAR;
-  else
-    filter = GL_NEAREST;
   gsk_gl_driver_create_render_target (self->gl_driver,
                                       width, height,
                                       filter, filter,
@@ -3534,7 +3551,7 @@ add_offscreen_ops (GskGLRenderer         *self,
   init_full_texture_region (texture_region_out, texture_id);
 
   if ((flags & NO_CACHE_PLZ) == 0)
-    gsk_gl_driver_set_texture_for_pointer (self->gl_driver, child_node, texture_id);
+    gsk_gl_driver_set_texture_for_key (self->gl_driver, &key, texture_id);
 
   return TRUE;
 }